﻿namespace DCHealthCheck.MvcWeb.Classes.DataServices {

    #region Usings
    using DCHealthCheck.Common;
    using DCHealthCheck.DomainModel;
    using DCHealthCheck.MvcWeb.Classes.ViewModels;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Text;
    using System.Threading;
    using System.Web;
    using System.Web.Mvc;
    #endregion

    public class ServiceInfoDataService : BaseDataService, IServiceInfoDataService {

        #region Members
        private readonly IRepository<ServiceInfo, IEntityManager> ServiceInfoRepository;

        private readonly Expression<Func<ServiceInfo, object>>[] ServiceInfoIncludedEntities;
        #endregion

        #region Constructor
        public ServiceInfoDataService(
            IRepository<DomainController, IEntityManager> dcRepository,
            IRepository<ServiceInfo, IEntityManager> serviceInfoRepository)
            : base(dcRepository) {
            if (serviceInfoRepository == null) {
                throw new ArgumentNullException("serviceInfoRepository");
            }

            this.ServiceInfoRepository = serviceInfoRepository;
            this.ServiceInfoIncludedEntities = new Expression<Func<ServiceInfo, object>>[] {
                (x) => x.SystemInfo,
                (x) => x.SystemInfo.DomainController,
                (x) => x.SystemInfo.ProcessInfos
            };
        }
        #endregion

        #region Methods
        private void BuildFilterSelectLists(ServiceInfoViewModel viewModel) {
            var filteredDCIds = this.GetFilteredServiceInfos(viewModel)
                .Select(x => x.SystemInfo.DomainController.Id)
                .Distinct()
                .ToList();

            base.BuildFilterSelectLists(filteredDCIds, viewModel);

            #region Create and add the default (Any) select list item
            var selectListItem = new SelectListItem();
            selectListItem.Text = "(Any)";
            selectListItem.Value = string.Empty;

            viewModel.ServiceNameSelectListItems.Add(selectListItem);
            viewModel.ServiceExecutableSelectListItems.Add(selectListItem);
            #endregion

            #region Build Service Names select list
            var serviceNames = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

            var serviceNamesList = this.GetFilteredServiceInfos(viewModel)
                .OrderBy(x => x.DisplayName)
                .Select(x => new { x.DisplayName, x.Name })
                .ToList();

            foreach (var serviceName in serviceNamesList) {
                if (!serviceNames.ContainsKey(serviceName.DisplayName)) {
                    serviceNames.Add(serviceName.DisplayName, serviceName.Name);
                }
            }

            foreach (var name in serviceNames) {
                selectListItem = new SelectListItem();
                selectListItem.Text = name.Key;

                if (!string.IsNullOrWhiteSpace(viewModel.ServiceName) && string.Equals(viewModel.ServiceName, name.Value, StringComparison.OrdinalIgnoreCase)) {
                    selectListItem.Selected = true;
                }

                selectListItem.Value = name.Value;
                viewModel.ServiceNameSelectListItems.Add(selectListItem);
            }
            #endregion

            var serviceExecutablePaths = new List<string>();
            var serviceExecutableNames = new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase);

            serviceExecutablePaths = this.GetFilteredServiceInfos(viewModel)
                .Select(x => x.PathName)
                .Distinct()
                .ToList();

            for (int index = 0; index < serviceExecutablePaths.Count; index++) {
                // remove double quotes from path names
                var path = serviceExecutablePaths[index].Replace("\"", string.Empty);

                #region validation
                if (string.IsNullOrWhiteSpace(path)) continue;

                if (path.Any(x => Path.GetInvalidPathChars().Contains(x))) {
                    continue;
                }
                #endregion

                string serviceExecutableName = string.Empty;

                if (path.StartsWith(@"C:\Windows\System32\svchost.exe ", StringComparison.CurrentCultureIgnoreCase)) {
                    serviceExecutableName = "svchost.exe";
                }
                else if (path.StartsWith(@"C:\Windows\System32\dllhost.exe ", StringComparison.CurrentCultureIgnoreCase)) {
                    serviceExecutableName = "dllhost.exe";
                }
                else {
                    if (path.EndsWith(".exe", StringComparison.CurrentCultureIgnoreCase)) {
                        serviceExecutableName = Path.GetFileName(path);
                    }
                    else if ((path.IndexOf(".exe", StringComparison.CurrentCultureIgnoreCase) > -1) && (path.IndexOf(".exe", StringComparison.CurrentCultureIgnoreCase) == path.LastIndexOf(".exe", StringComparison.CurrentCultureIgnoreCase))) {
                        path = path.Substring(0, path.IndexOf(".exe", StringComparison.CurrentCultureIgnoreCase) + ".exe".Length);
                        serviceExecutableName = Path.GetFileName(path);
                    }
                    else {
                        serviceExecutableName = path;
                    }
                }

                if (!string.IsNullOrWhiteSpace(serviceExecutableName)) {
                    if (!serviceExecutableNames.ContainsKey(serviceExecutableName)) {
                        serviceExecutableNames.Add(serviceExecutableName, path);
                    }
                }
            }

            foreach (var serviceExecutable in serviceExecutableNames.Keys) {
                selectListItem = new SelectListItem();
                selectListItem.Text = serviceExecutable;
                if (!string.IsNullOrWhiteSpace(viewModel.ServiceExecutable) && string.Equals(viewModel.ServiceExecutable, serviceExecutable, StringComparison.OrdinalIgnoreCase)) {
                    selectListItem.Selected = true;
                }
                selectListItem.Value = serviceExecutableNames[serviceExecutable];
                viewModel.ServiceExecutableSelectListItems.Add(selectListItem);
            }
        }

        private IQueryable<ServiceInfo> GetFilteredServiceInfos(ServiceInfoViewModel viewModel) {

            var serviceInfos = this.ServiceInfoRepository.Get(this.ServiceInfoIncludedEntities);

            if (viewModel.SystemInfoId != Guid.Empty) {
                serviceInfos = serviceInfos.Where(x => x.SystemInfo.Id == viewModel.SystemInfoId);
            }
            else {
                if (viewModel.DomainControllerId != Guid.Empty) {
                    serviceInfos = serviceInfos.Where(x => x.SystemInfo.DomainController.Id == viewModel.DomainControllerId);
                }

                if (viewModel.Date != DateTime.MinValue) {
                    serviceInfos = serviceInfos.Where(x => x.SystemInfo.CheckDateTimeUTC == viewModel.Date);
                }
                else {
                    if (viewModel.StartDate.HasValue) {
                        serviceInfos = serviceInfos.Where(x => x.SystemInfo.CheckDateUTC >= viewModel.StartDate);
                    }
                    if (viewModel.EndDate.HasValue) {
                        serviceInfos = serviceInfos.Where(x => x.SystemInfo.CheckDateUTC <= viewModel.EndDate);
                    }
                }

                if (!string.IsNullOrWhiteSpace(viewModel.ADSite)) {
                    serviceInfos = serviceInfos
                        .Where(x => x.SystemInfo.DomainController.ADSite == viewModel.ADSite);
                }
                if (!string.IsNullOrWhiteSpace(viewModel.Domain)) {
                    serviceInfos = serviceInfos
                        .Where(x => x.SystemInfo.DomainController.NetbiosDomain == viewModel.Domain);
                }
                if (!string.IsNullOrWhiteSpace(viewModel.OSVersion)) {
                    serviceInfos = serviceInfos
                        .Where(x => x.SystemInfo.DomainController.OSVersion == viewModel.OSVersion);
                }
                if (!string.IsNullOrWhiteSpace(viewModel.DCName)) {
                    serviceInfos = serviceInfos
                        .Where(x => x.SystemInfo.DomainController.SamAccountName == viewModel.DCName);
                }

                if (!string.IsNullOrWhiteSpace(viewModel.ServiceName)) {
                    serviceInfos = serviceInfos.Where(x => x.Name == viewModel.ServiceName);
                }

                if (!string.IsNullOrWhiteSpace(viewModel.ServiceExecutable)) {
                    serviceInfos = serviceInfos.Where(x => x.PathName.ToUpper().Contains(viewModel.ServiceExecutable.ToUpper()));
                }

                if (viewModel.ProcessId > 0) {
                    serviceInfos = serviceInfos.Where(x => x.ProcessId == viewModel.ProcessId);
                }
            } // else if (this.SystemInfoId != Guid.Empty)

            return serviceInfos;

        }

        public void ProcessData(ServiceInfoViewModel viewModel, bool initialize = true, bool buildFilterSelectLists = true) {
            Global.DefaultLog.DebugFormat("[ThreadId: {0}] {1}", Thread.CurrentThread.ManagedThreadId, Extensions.CurrentMethodName());

            if (viewModel == null) {
                throw new ArgumentNullException("viewModel");
            }

            try {
                if (initialize) {
                    viewModel.Initialize();
                }
                var serviceInfos = this.GetFilteredServiceInfos(viewModel);
                viewModel.DisplayTotalResults = serviceInfos.Count();

                #region Skip count, take, and sorting
                if (!string.IsNullOrWhiteSpace(viewModel.SortColumn)) {
                    if (viewModel.CurrentSortDirection == System.Web.Helpers.SortDirection.Ascending) {
                        viewModel.ServiceInfos = serviceInfos
                            .OrderBy(viewModel.SortColumn)
                            .Skip(viewModel.SkipCount.Value)
                            .Take(viewModel.TakeCount.Value)
                            .ToList();
                    }
                    else {
                        viewModel.ServiceInfos = serviceInfos
                            .OrderByDescending(viewModel.SortColumn)
                            .Skip(viewModel.SkipCount.Value)
                            .Take(viewModel.TakeCount.Value)
                            .ToList();
                    }
                }
                else {
                    viewModel.ServiceInfos = serviceInfos
                        .OrderBy(x => x.DisplayName)
                        .Skip(viewModel.SkipCount.Value)
                        .Take(viewModel.TakeCount.Value)
                        .ToList();
                }
                #endregion

                viewModel.GetPageCounts();
                if (buildFilterSelectLists) {
                    this.BuildFilterSelectLists(viewModel);
                }
            }
            catch (Exception e) {
                Global.DefaultLog.DebugFormat("[ThreadId: {0}] {1} {2}", Thread.CurrentThread.ManagedThreadId, Extensions.CurrentMethodName(), e.VerboseExceptionString());
            }
        }

        public string GetCSVData(ServiceInfoViewModel viewModel) {

            viewModel.Initialize();
            viewModel.SkipCount = 0;
            viewModel.TakeCount = int.MaxValue;
            this.ProcessData(viewModel, false, false);

            var fileData = new StringBuilder();
            fileData.AppendLine(ServiceInfo.CSVHeader);
            foreach (var serviceInfo in viewModel.ServiceInfos) {
                fileData.AppendLine(serviceInfo.ToCSVString());
            }

            return fileData.ToString();
        }
        #endregion

    }
}